home *** CD-ROM | disk | FTP | other *** search
/ Info-Mac 4 / Info_Mac IV CD-ROM (Pacific HiTech Inc.)(August 1994).iso / Development / Source / Watch Volume Mount / WatchMount.cc < prev    next >
Text File  |  1994-03-08  |  10KB  |  284 lines

  1. /*
  2.  ***********************************************************************
  3.  *
  4.  *             MountVol interception to find and display a MOTD file
  5.  *
  6.  * The purpose of this stuff is to detect when a new volume is being
  7.  * mounted (in any possible way: by inserting a floppy, using the Chooser, or
  8.  * clicking on the alias). If a new volume is detected, we check to see if
  9.  * there is a Message-of-the-day file at some "standard" location on that volume.
  10.  * If there is, we get the TeachTech (or whatever other application-creator
  11.  * of the MOTD file) to show the file to the user.
  12.  * At the start-up, we do the similar MOTD seeking thing for each mounted volume.                    
  13.  *
  14.  * The way we go about this, is patching the _MountVol system trap. The _MountVol
  15.  * is entered whenever the system needs to mount a volume. Our patch saves
  16.  * the driver ref number for the drive that contains the volume. Our main
  17.  * application dozes in the background mode, but wakes up from time to time
  18.  * to check to see if a new volume has been mounted since the last check.
  19.  * If it has, the application gets the TeachTech to show the MOTD file
  20.  * (if it can be located, of course).
  21.  *
  22.  * The path to the MOTD file is expected to be specified as a 'STR ' resource
  23.  * named 'MOTD Path'. Note, the path name should be a partial file name: for
  24.  * obvious reasons, the path name to the MOTD file should not contain the
  25.  * volume name (because it is searched on several volumes). So, the path
  26.  * name should always start with a colon, for example
  27.  *        :PUBLIC:Notices:Motd.MAC
  28.  * Make sure that the TeachTech is the creator of the MOTD file (you can use
  29.  * any other editor/viewer, if you wish so).
  30.  *
  31.  ***********************************************************************
  32.  */
  33.  
  34. /* MacHeaders Included */
  35. #include "myenv.h"
  36. #include <string.h>
  37. #include <AppleEvents.h>
  38.  
  39. /*
  40.  *----------------------------------------------------------------------
  41.  *                Installing our _MountVol interceptor
  42.  * Note, the interceptor is a CODE resource, compiled separately. It
  43.  * is assumed to be put into the resource fork of the application. See
  44.  * the file "MountVol init.c" for more details.
  45.  * Note, the resource has to be loaded in the system heap, and be protected
  46.  * and locked from the outset (because it is a system "patch").
  47.  *
  48.  * Attention! Don't use 'NSetTrapAddress()' to set up the trap address.
  49.  * It works, but it does more than it's supposed to: besides setting the trap
  50.  * address to the new trap handler (our interceptor), it sets some flags in
  51.  * the process context. So, when the process manager backgrounds our application, it
  52.  * _RESTORES_ all the system traps the application has changed to their
  53.  * default values! But we need our interceptor to run even when the application
  54.  * is in the background!
  55.  */
  56.  
  57.                                     // Sets up a new handler for the specified
  58.                                     // system trap.
  59.                                     // It's a brute force method (messing directly
  60.                                     // with the system trap table), but it seems to
  61.                                     // be the only way to be sure the handler we
  62.                                     // installed sticks (even when the application
  63.                                     // is switched to the background).
  64. static void my_set_system_trap(ProcPtr handler, const int trap_word)
  65. {
  66.     assure( trap_word & 0x8ff, "The trap word looks like a toolbox, not a system trap");
  67.     ProcPtr * dispatch_table = (ProcPtr *)0x400;    // System Trap dispatch table
  68.     dispatch_table[trap_word & 0xff] = handler;
  69.                                             // Make sure we really have set the trap
  70.     assert( (ProcPtr)NGetTrapAddress(trap_word & 0xff,OSTrap) == handler );
  71. }
  72.  
  73.                                     // "Identification" of our interceptor
  74.                                     // (code resource)
  75. #define    Interceptor_type    'Init'                // Code resource type
  76. #define    Interceptor_name    "\pMyMountVol"        // Code resource name
  77. #define Interceptor_sig     23547                // Interceptor's signature
  78.     
  79. #define _MountVol     0xA00F            // _MountVol trap
  80.  
  81. struct MyMountVolLayout
  82. {
  83.     short    bra_instr;                // Entry point: jump around data instruction
  84.     short    signature;                // To make sure it is what it is
  85.     short    driver_ref_no;            // Intercepted driver ref number
  86.     ProcPtr real_trap_handler;        // Ptr to the real trap program
  87. };                                    // The rest is the code for the "interception", of
  88.                                     // no interest to us here
  89.  
  90. class MountVolIntercept
  91. {
  92.     MyMountVolLayout ** interceptor;
  93.     short last_mounted_drive;            // Drive ref number for the last mounted drive
  94.                                         // detected
  95.         
  96. public:
  97.     MountVolIntercept(void);
  98.     ~MountVolIntercept(void);
  99.     Boolean has_caught(void);
  100.     short last_driver_ref_no(void) const { return last_mounted_drive; }
  101. };
  102.  
  103.  
  104.                                         // Install the interceptor
  105. MountVolIntercept::MountVolIntercept(void)
  106. {
  107.                                         // Load our interceptor and make sure we
  108.                                         // got what we need
  109.     interceptor = (MyMountVolLayout **)Get1NamedResource(Interceptor_type,Interceptor_name);
  110.     assert( interceptor != nil );
  111.     int interceptor_attr = GetResAttrs((Handle)interceptor);
  112.     do_well( ResError() );
  113.     assert( interceptor_attr & resSysHeap );        // Make sure the resource is locked,
  114.     assert( interceptor_attr & resLocked );            // protected, and in the sytem heap
  115.     assert( interceptor_attr & resProtected );
  116.     assert( (*interceptor)->signature == Interceptor_sig );
  117.                                             // Since we're going to deinstall the interceptor
  118.                                             // when we quit, we don't need to detach the resource
  119.     
  120.     (*interceptor)->real_trap_handler = (ProcPtr)NGetTrapAddress(_MountVol & 0xff,OSTrap);
  121.     my_set_system_trap((ProcPtr)*interceptor,_MountVol);
  122.  
  123.     last_mounted_drive = 0;
  124. }
  125.  
  126.                                         // Uninstall the interceptor and clean up
  127.                                         // after ourselves
  128. MountVolIntercept::~MountVolIntercept(void)
  129. {
  130.     assert( interceptor != 0 && *interceptor != 0 );
  131.     my_set_system_trap((*interceptor)->real_trap_handler,_MountVol);
  132.  
  133.     ReleaseResource((Handle)interceptor);
  134.     // DisposeHandle((Handle)interceptor); <-- use this only if resource was detached
  135.     notify_and_wait("Quitting: _MountVol trap is restored");
  136. }
  137.  
  138.                                         // Check to see if the interceptor has caught
  139.                                         // smth. If it has, save the intercepted drive
  140.                                         // ref number.
  141. Boolean MountVolIntercept::has_caught(void)
  142. {
  143.     short& caught_ref_no = (*interceptor)->driver_ref_no;
  144.     if( caught_ref_no == 0 )
  145.       return FALSE;
  146.       
  147.     last_mounted_drive = caught_ref_no;
  148.     caught_ref_no = 0;
  149.     return TRUE;
  150. }
  151.  
  152. /*
  153.  *----------------------------------------------------------------------
  154.  *                A class that takes care of displaying the message
  155.  *                                of the day file
  156.  * First, we try to locate the file at the "standard" location on a given
  157.  * volume. If we got it, we use the 'open_selection()' function to "double-click"
  158.  * on the file by sending a message to the Finder. He does the rest.
  159.  */
  160.  
  161.  
  162. class DisplayMOTD
  163. {
  164.     StringPtr * path_name_handle;
  165.     FSSpec         motd_fsspec;
  166.     
  167. public:
  168.     DisplayMOTD(void);
  169.     ~DisplayMOTD(void) {}
  170.     void display_on_volume(const short drive_ref_no);
  171. };
  172.  
  173.                                         // Figure out the "standard" location of the
  174.                                         // MOTD file (from the 'STR ' resource)
  175. DisplayMOTD::DisplayMOTD(void)
  176. {
  177.     const unsigned char * path_rsrc_name = "\pMOTD Path";
  178.     path_name_handle = (StringPtr *)Get1NamedResource('STR ',path_rsrc_name);
  179.     assert( path_name_handle != nil );
  180.     assure( (*path_name_handle)[1] == ':', "The 'MOTD Path' should begin with a colon");
  181. }
  182.  
  183. void open_selection(const FSSpec& file_spec);
  184.  
  185.                                         // Find the MOTD file on a given volume and
  186.                                         // "open_select" it. 
  187. void DisplayMOTD::display_on_volume(const short drive_ref_no)
  188. {
  189.     Str255 volume_name;
  190.     short vRefNum;
  191.     long free_bytes;
  192.     OSErr err;
  193.     if( (err = GetVInfo(drive_ref_no,volume_name,&vRefNum,&free_bytes)) != noErr )
  194.     {
  195.       notify("Error %d getting the volume info for the drive #%d",err,drive_ref_no);
  196.       return;
  197.     }
  198.            
  199.     if( FSMakeFSSpec(vRefNum,0,*path_name_handle,&motd_fsspec) != noErr )
  200.     {
  201.       // notify("MOTD file was not found on the volume %#s",volume_name);
  202.       return;
  203.     }
  204.     
  205.     open_selection(motd_fsspec);
  206. }
  207.  
  208. /*
  209.  *----------------------------------------------------------------------
  210.  *                Where everything happens - THE event loop
  211.  */
  212.  
  213.                                             // Handle a Quit event
  214.                                             // For now, pretend that we handled it
  215.                                             // and everything went fine
  216. static pascal OSErr Handle_AppleEvent_quit
  217.     (AppleEvent * event, AppleEvent * reply, long refcon)
  218. {
  219.     return noErr;
  220. }
  221.  
  222.                                         // Check out already mounted volumes to see
  223.                                         // if they contain MOTD
  224.                                         // Note drive_ref_numbers for mounted volumes
  225.                                         // don't have to be consecutive
  226. static void check_already_mounted_vols(DisplayMOTD& motd_object)
  227. {
  228.     const int max_mounted_vols = 10;            // Check first few drives
  229.     register int drive_ref_no;
  230.     Str255 volume_name;
  231.     short vRefNum;
  232.     long free_bytes;
  233.     OSErr err;
  234.     
  235.     for(drive_ref_no=1; drive_ref_no<=max_mounted_vols; drive_ref_no++)
  236.       if( (err=GetVInfo(drive_ref_no,volume_name,&vRefNum,&free_bytes)) == noErr )
  237.         motd_object.display_on_volume(drive_ref_no);
  238. }
  239.  
  240. void main(void)
  241. {
  242.   Initialize_MAC();
  243.                                             // Claim that we can handle a Quit event
  244.   do_well( AEInstallEventHandler(kCoreEventClass,kAEQuitApplication,
  245.                                   (ProcPtr)Handle_AppleEvent_quit,0,FALSE) );
  246.  
  247.   DisplayMOTD motd_object;
  248.   check_already_mounted_vols(motd_object);
  249. //  motd_object.display_on_volume(3);
  250. //  notify_and_wait("Done");
  251.  
  252.   MountVolIntercept intercept_object;
  253.     
  254.   const long event_timeout = 20;
  255.   EventRecord    theEvent;
  256.  
  257.   for(;;)
  258.   {
  259.     WaitNextEvent(everyEvent, &theEvent,event_timeout,nil);
  260.     switch (theEvent.what)
  261.     {
  262.       case nullEvent:
  263.               if( intercept_object.has_caught() )
  264.                 motd_object.display_on_volume(intercept_object.last_driver_ref_no());
  265.               continue;
  266.                  
  267.       case keyDown:                    // Terminate if any key was pressed
  268.              break;
  269.  
  270.       case 23:                                // it is nothing but 'kHighLevelEvent'
  271.            OSErr err;
  272.            if( (err=AEProcessAppleEvent(&theEvent)) == noErr )
  273.              break;                            // It was a Quit event - the only one which 
  274.            else if( err != errAEEventNotHandled )    // could be handled
  275.             notify("Error %d in processing an Apple Event",err);
  276.            continue;                            // NotHandled events are just ignored
  277.           
  278.       default:                        // Ignore all other events
  279.              continue;
  280.      }
  281.      break;
  282.   }
  283. }
  284.